/**
 * Provides the Game State for GOC
 *
 * @file src/GameState.cpp
 * @author Jacob Dix
 * @date 20120519 - Initial Release
 */
#include "GameState.hpp"

#include <GQE/Core/assets/FontAsset.hpp>
#include <GQE/Core/assets/ImageAsset.hpp>
#include <GQE/Core/interfaces/IApp.hpp>
#include <GQE/Entity/classes/Prototype.hpp>
#include <GQE/Entity/classes/Instance.hpp>
#include <GQE/Entity/classes/PrototypeManager.hpp>
GQE::PrototypeManager GameState::gPrototypeManager;
GameState::GameState(GQE::IApp& theApp) :
  GQE::IState("Game",theApp),
  mAnimationSystem(theApp),
  mMovementSystem(theApp),
  mRenderSystem(theApp),
  mControlSystem(theApp),
  mCollisionSystem(theApp,this),
  mActionSystem(theApp),
  mFloorTile("FloorTile",0),
  mFloorTileMask("FloorTileMask",1),
  mMidTile("MidTile",3),
  mWarp("Warp",2),
  mMidTileMask("MidTileMask",4),
  mUpperTile("UpperTile",5),
  mUpperTileMask("UpperTileMask",6),
  mCharacter("Character",2),
  mAttack("Attack",2),
  mItem("Item",2),
  mDirtTileset("resources/Images/Dirt.png",GQE::AssetLoadNow),
  mPlayerImage("resources/Images/Player.png",GQE::AssetLoadNow),
  mWallTileset("resources/Images/TempleWalls.png",GQE::AssetLoadNow),
  mWaterTileset("resources/Images/Water.png",GQE::AssetLoadNow),
  mFont("resources/arial.ttf",GQE::AssetLoadNow),
  mMusic("resources/Music/Peak.ogg",GQE::AssetLoadNow),
  mPlayer(NULL),
  mGUITiles("resources/Images/GUI.png",GQE::AssetLoadNow)
{
}

GameState::~GameState(void)
{
  ILOG() << "GameState::dtor()" << std::endl;
}

void GameState::DoInit(void)
{
  // First call our base class implementation
  IState::DoInit();
  srand(time(NULL));
  mApp.SetUpdateRate(60.0f);
  mApp.mWindow.setFramerateLimit(60.0f);
  mApp.mStatManager.SetShow(true);

  // Add various systems to the FloorTile prototype
  mFloorTile.AddSystem(&mRenderSystem);
  mFloorTile.mProperties.Add<std::string>("sTileset","");
  //gPrototypeManager.AddPrototype(&mFloorTile);

  // Add various systems to the FloorTileMask prototype
  mFloorTileMask.AddSystem(&mCollisionSystem);
  mFloorTileMask.AddSystem(&mRenderSystem);
  mFloorTileMask.mProperties.Add<std::string>("sTileset","");
  //gPrototypeManager.AddPrototype(&mFloorTileMask);

  // Add various systems to the MidTile prototype
  mMidTile.AddSystem(&mCollisionSystem);
  mMidTile.AddSystem(&mRenderSystem);
  mMidTile.mProperties.Add<std::string>("sTileset","");
  //gPrototypeManager.AddPrototype(&mMidTile);
  
  // Add various systems to the MidTileMask prototype
  mMidTileMask.AddSystem(&mCollisionSystem);
  mMidTileMask.AddSystem(&mRenderSystem);
  mMidTileMask.mProperties.Add<std::string>("sTileset","");
  //gPrototypeManager.AddPrototype(&mMidTileMask);
  
  // Add various systems to the UpperTile prototype
  mUpperTile.AddSystem(&mCollisionSystem);
  mUpperTile.AddSystem(&mRenderSystem);
  mUpperTile.mProperties.Add<std::string>("sTileset","");
  //gPrototypeManager.AddPrototype(&mUpperTile);
  
  // Add various systems to the UpperTileMask prototype
  mUpperTileMask.AddSystem(&mCollisionSystem);
  mUpperTileMask.AddSystem(&mRenderSystem);
  mUpperTileMask.mProperties.Add<std::string>("sTileset","");
  //gPrototypeManager.AddPrototype(&mUpperTileMask);
  
  // Add various systems to the Arrow prototype
  mAttack.AddSystem(&mCollisionSystem);
  mAttack.AddSystem(&mRenderSystem);
  //gPrototypeManager.AddPrototype(&mArrow);

  // Add various systems to the Character prototype
  mCharacter.AddSystem(&mAnimationSystem);
  mCharacter.AddSystem(&mMovementSystem);
  mCharacter.AddSystem(&mCollisionSystem);
  mCharacter.AddSystem(&mRenderSystem);
  mCharacter.AddSystem(&mActionSystem);
  mCharacter.mProperties.Add<bool>("bMovable", true);
  // Add various systems to the Character prototype
  mWarp.AddSystem(&mCollisionSystem);
  mWarp.AddSystem(&mRenderSystem);
	//debug code
	GQE::ConfigAsset anConfigAsset(GQE::IApp::APP_SETTINGS,GQE::AssetLoadNow);
  // Now load the map from our TMX file
  LoadMap(anConfigAsset.GetAsset().GetString("map","startmap",""));
#if SFML_VERSION_MAJOR<2		
  mMusic.GetAsset().SetLoop(true);
  mMusic.GetAsset().Play();
#else
  mMusic.GetAsset().setLoop(true);
  mMusic.GetAsset().play();
#endif

  mInterface.setTexture(mGUITiles.GetAsset());
  mInterface.setPosition(0,384);
}

void GameState::ReInit()
{
}

void GameState::HandleEvents(sf::Event theEvent)
{
}

void GameState::UpdateFixed(void)
{
  mControlSystem.UpdateFixed();
  mMovementSystem.UpdateFixed();
  mCollisionSystem.UpdateFixed();
  mAnimationSystem.UpdateFixed();
}

void GameState::UpdateVariable(float theElapsedTime)
{
}

void GameState::Draw(void)
{
#if SFML_VERSION_MAJOR<2
  mApp.mWindow.Clear(sf::Color(150,150,150,255));
  mApp.mWindow.Draw(mInterface);
#else
  mApp.mWindow.clear(sf::Color(150,150,150,255));
  mApp.mWindow.draw(mInterface);
#endif
  mRenderSystem.Draw();
  mCollisionSystem.Draw();
}

void GameState::LoadMap(std::string theFilename)
{
	//clear old map.
	mFloorTile.Destroy();
	mFloorTileMask.Destroy();
	mMidTile.Destroy();
	mMidTileMask.Destroy();
	mUpperTile.Destroy();
	mUpperTileMask.Destroy();
	mWarp.Destroy();
	std::map< std::string, std::string > anLayerPropertyList;
	std::map< std::string, std::string > anTilesetPropertyList;
	std::map< std::string, std::string > anTilePropertyList;
  TmxAsset anMapAsset = TmxAsset(theFilename,GQE::AssetLoadNow);
  Tmx::Map& anMap = anMapAsset.GetAsset();
  int anMapWidth=anMap.GetWidth();
  int anMapHeight=anMap.GetHeight();
  int anTileWidth=0;
  int anTileHeight=0;
  for(int l = 0;l < anMap.GetNumLayers();l++)
  {
    const Tmx::Layer* anLayer = anMap.GetLayer(l);
		anLayerPropertyList = anLayer->GetProperties().GetList();
    for(int x=0;x<anMap.GetWidth();x++)
    {
      for(int y=0;y<anMap.GetHeight();y++)
      {
        const Tmx::MapTile anMapTile=anLayer->GetTile(x,y);
        if(anMapTile.tilesetId >= 0)
        {
          const Tmx::Tileset *anTileset=anMap.GetTileset(anMapTile.tilesetId);
          if(anTileset!=NULL)
          {
						anTilesetPropertyList = anTileset->GetProperties().GetList();
          }
          const Tmx::Tile *anTile = anTileset->GetTile(anMapTile.id);
          if(anTile!=NULL)
          {
						anTilePropertyList = anTile->GetProperties().GetList();
          }
          anTileWidth=anTileset->GetTileWidth();
          anTileHeight=anTileset->GetTileHeight();
          GQE::Instance* anInstance = NULL;
          if(anLayer->GetName()=="FloorLayer")
          {
						anInstance=mFloorTile.MakeInstance();
					}
					if(anLayer->GetName()=="FloorLayerMask")
          {
						anInstance=mFloorTileMask.MakeInstance();
					}
					if(anLayer->GetName()=="MidLayer")
          {
						anInstance=mMidTile.MakeInstance();
					}
					if(anLayer->GetName()=="MidLayerMask")
          {
						anInstance=mMidTileMask.MakeInstance();
					}
					if(anLayer->GetName()=="UpperLayer")
          {
						anInstance=mUpperTile.MakeInstance();
					}
					if(anLayer->GetName()=="UpperLayerMask")
          {
						anInstance=mUpperTileMask.MakeInstance();
					}
					if(anInstance!=NULL)
					{
						anInstance->mProperties.Set<sf::Vector2f>("vPosition",sf::Vector2f(x*anTileWidth,y*anTileHeight));
						ParseProperties(anInstance,anLayerPropertyList);
						ParseProperties(anInstance,anTilesetPropertyList);
						ParseProperties(anInstance,anTilePropertyList);
						if(anInstance->mProperties.Get<std::string>("sTileset")==mWaterTileset.GetFilename())
						{
							anInstance->mProperties.Set<sf::Sprite>("Sprite",sf::Sprite(mWaterTileset.GetAsset()));
							#if SFML_VERSION_MAJOR<2
								anInstance->mProperties.Set<sf::IntRect>("rSpriteRect",
										sf::IntRect((anMapTile.id%int(mWaterTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth,
										sf::IntRect((anMapTile.id%int(mWaterTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth,
											(anMapTile.id/int(mWaterTileset.GetAsset().GetWidth()/anTileHeight))*anTileWidth,
											(anMapTile.id%int(mWaterTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth+anTileWidth,
											(anMapTile.id/int(mWaterTileset.GetAsset().GetWidth()/anTileHeight))*anTileWidth+anTileHeight));
							#else
								anInstance->mProperties.Set<sf::IntRect>("rSpriteRect",
										sf::IntRect(anMapTile.id%int(mWaterTileset.GetAsset().getSize().x/anTileWidth)*anTileWidth,
											anMapTile.id/int(mWaterTileset.GetAsset().getSize().x/anTileWidth)*anTileWidth,anTileWidth,anTileHeight));
							#endif
						}
						if(anInstance->mProperties.Get<std::string>("sTileset")==mWallTileset.GetFilename())
						{
							anInstance->mProperties.Set<sf::Sprite>("Sprite",sf::Sprite(mWallTileset.GetAsset()));
							#if SFML_VERSION_MAJOR<2
								anInstance->mProperties.Set<sf::IntRect>("rSpriteRect",
										sf::IntRect((anMapTile.id%int(mWallTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth,
											(anMapTile.id/int(mWallTileset.GetAsset().GetWidth()/anTileHeight))*anTileWidth,
											(anMapTile.id%int(mWallTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth+anTileWidth,
											(anMapTile.id/int(mWallTileset.GetAsset().GetWidth()/anTileHeight))*anTileWidth+anTileHeight));
							#else
								anInstance->mProperties.Set<sf::IntRect>("rSpriteRect",
										sf::IntRect(anMapTile.id%int(mWallTileset.GetAsset().getSize().x/anTileWidth)*anTileWidth,
											anMapTile.id/int(mWallTileset.GetAsset().getSize().x/anTileWidth)*anTileWidth,anTileWidth,anTileHeight));
							#endif

						}
            if(anInstance->mProperties.Get<std::string>("sTileset")==mDirtTileset.GetFilename())
						{
							anInstance->mProperties.Set<sf::Sprite>("Sprite",sf::Sprite(mDirtTileset.GetAsset()));
							#if SFML_VERSION_MAJOR<2
								anInstance->mProperties.Set<sf::IntRect>("rSpriteRect",
										sf::IntRect((anMapTile.id%int(mDirtTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth,
											(anMapTile.id/int(mDirtTileset.GetAsset().GetWidth()/anTileHeight))*anTileWidth,
											(anMapTile.id%int(mDirtTileset.GetAsset().GetWidth()/anTileWidth))*anTileWidth+anTileWidth,
											(anMapTile.id/int(mDirtTileset.GetAsset().GetWidth()/anTileHeight))*anTileWidth+anTileHeight));
							#else
								anInstance->mProperties.Set<sf::IntRect>("rSpriteRect",
										sf::IntRect(anMapTile.id%int(mDirtTileset.GetAsset().getSize().x/anTileWidth)*anTileWidth,
											anMapTile.id/int(mDirtTileset.GetAsset().getSize().x/anTileWidth)*anTileWidth,anTileWidth,anTileHeight));
							#endif

						}
						
					}
					anTilesetPropertyList.clear();
					anTilePropertyList.clear();
        }
      }
    }
  }

  for(int o=0;o<anMap.GetNumObjectGroups();o++)
  {
    const Tmx::ObjectGroup* anObjectGroup=anMap.GetObjectGroup(o);
    for (int j = 0; j < anObjectGroup->GetNumObjects(); ++j)
    {
      const Tmx::Object* anObject=anObjectGroup->GetObject(j);
			anTilePropertyList=anObject->GetProperties().GetList();
      if(anObject->GetName()=="Player" && mPlayer==NULL)
      {
        mPlayer = mCharacter.MakeInstance();
        
        ParseProperties(mPlayer,anTilePropertyList);
        if(mPlayer!=NULL)
        {
          mControlSystem.AddEntity(mPlayer);
#if SFML_VERSION_MAJOR<2
          mPlayer->mProperties.Set<sf::Sprite>("Sprite",
              sf::Sprite(mPlayerImage.GetAsset()));
          mPlayer->mProperties.Set<sf::IntRect>("rBoundingBox",sf::IntRect(12,12,20,30));
#else
          mPlayer->mProperties.Set<sf::Sprite>("Sprite",
              sf::Sprite(mPlayerImage.GetAsset(),sf::IntRect(0,32,32,32)));
          mPlayer->mProperties.Set<sf::IntRect>("rBoundingBox",sf::IntRect(10,12,10,18));
#endif
					mPlayer->mProperties.Set<bool>("bDebugDraw",false);
          mPlayer->mProperties.Set<sf::IntRect>("rSpriteRect",sf::IntRect(0,0,32,32));
          mPlayer->mProperties.Set<sf::Vector2f>("vPosition",sf::Vector2f(anObject->GetX(),anObject->GetY()));
          mPlayer->mProperties.Set<float>("fFrameDelay",0.25f);
          mPlayer->mProperties.Set<sf::IntRect>("rFrameRect",sf::IntRect(0,0,96,128));
          mPlayer->mProperties.Set<bool>("bPlayer",true);
          mPlayer->mProperties.Set<sf::Vector2f>("vStartPosition",
            sf::Vector2f(anObject->GetX(),anObject->GetY()));
          mPlayer->mProperties.Set<sf::Vector2f>("vOrigin",
            sf::Vector2f(0,0));
          // Make note of starting position for other characters too
          mCharacter.mProperties.Set<sf::Vector2f>("vStartPosition",
            sf::Vector2f(anObject->GetX(),anObject->GetY()));
        }
      }
      if(anObject->GetType()=="Warp")
      {
        GQE::Instance* anWarp = mWarp.MakeInstance();
        if(anWarp!=NULL)
        {
					anWarp->mProperties.Add<std::string>("sMapFilename","");
					anWarp->mProperties.Add<sf::Vector2f>("vDestPosition",sf::Vector2f(0,0));
					
					ParseProperties(anWarp,anLayerPropertyList);
					ParseProperties(anWarp,anTilePropertyList);
#if SFML_VERSION_MAJOR<2
          anWarp->mProperties.Set<sf::IntRect>("rBoundingBox",sf::IntRect(0,0,anObject->GetWidth(),anObject->GetHeight()));
#else
          anWarp->mProperties.Set<sf::IntRect>("rBoundingBox",sf::IntRect(0,0,anObject->GetWidth(),anObject->GetHeight()));
#endif
					anWarp->mProperties.Set<bool>("bDebugDraw",false);
          anWarp->mProperties.Set<sf::Vector2f>("vPosition",sf::Vector2f(anObject->GetX(),anObject->GetY()));
          anWarp->mProperties.Set<bool>("bWarp",true);
          anWarp->mProperties.Set<bool>("bVisible",false);
          anWarp->mProperties.Set<sf::Vector2f>("vOrigin",
            sf::Vector2f(0,0));
        }
      }
    }
  }
}
void GameState::ParseProperties(GQE::IEntity* theInstance,std::map<std::string,std::string,std::less<std::string> > thePropertyList)
{
	if(theInstance!=NULL)
	{
		std::map<std::string,std::string,std::less<std::string> >::iterator anIter;
		for(anIter=thePropertyList.begin();anIter!=thePropertyList.end();++anIter)
		{
			std::string anName=anIter->first;
			std::string anValue=anIter->second;
			if(anName[0]=='b')
			{
				theInstance->mProperties.Set<bool>(anName,GQE::ParseBool(anValue,false));
			}
			else if(anName[0]=='c')
			{
				theInstance->mProperties.Set<sf::Color>(anName,GQE::ParseColor(anValue,sf::Color(0,0,0)));
			}
			else if(anName[0]=='d')
			{
				theInstance->mProperties.Set<double>(anName,GQE::ParseDouble(anValue,0.0f));
			}
			else if(anName[0]=='f')
			{
				theInstance->mProperties.Set<float>(anName,GQE::ParseFloat(anValue,0.0f));
			}
			else if(anName[0]=='i')
			{
				theInstance->mProperties.Set<GQE::Int32>(anName,GQE::ParseInt32(anValue,0));
			}
			else if(anName[0]=='r')
			{
				theInstance->mProperties.Set<sf::IntRect>(anName,GQE::ParseIntRect(anValue,sf::IntRect(0,0,0,0)));
			}
			else if(anName[0]=='u')
			{
				theInstance->mProperties.Set<GQE::Uint32>(anName,GQE::ParseUint32(anValue,0));
			}
			else if(anName[0]=='v')
			{
				theInstance->mProperties.Set<sf::Vector2f>(anName,GQE::ParseVector2f(anValue,sf::Vector2f(0.0f,0.0f)));
			}
			else if(anName[0]=='z')
			{
				theInstance->mProperties.Set<sf::Vector3f>(anName,GQE::ParseVector3f(anValue,sf::Vector3f(0.0f,0.0f,0.0f)));
			}
			else
			{
				theInstance->mProperties.Set<std::string>(anName,anValue);
			}
		}
	}
}
void GameState::HandleCleanup(void)
{
}

const GQE::PrototypeManager& GameState::GetPrototypeManager()
{
  return gPrototypeManager;
}
/**
 * Copyright (c) 2010-2011 Ryan Lindeman
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
